\chapter{系统加载}

在上一章我们的程序已经取得了机器的控制权，但是他只能做些无聊的事情：打印些东西而后空转。本章它将开始并完成的它的历史使命“加载内核并运行“，不过这个内核会它一样无聊，还是打印些东西然后空转。不过这确实巨大的进步，因为我们的程序已经从汇编变成了带有C的汇编，而且被加载的程序不再受到512字节的限制，它有着无限的可能性。
\section{PC启动程序}
\subsection{读磁盘}
\subsection{ELF文件格式}
\subsection{准备一个ELF格式的内核}
\subsection{开启20位寻址}
系统开机以后，CPU运行于实模式下且仅仅支持16位寻址。这就意味着我们最多可以使用64K的内存。对于bootsect.S的内部处理这个空间应该够用了，但如果想要加载vmlinux这个巨大的程序可以远远不够的。为此我们需要将寻址空间提高的20位，即1M。这也意味着我们的操作系统最多也就只能有1M的大小，这对于一个没有什么驱动程序的操作系统来说足够用了。
\subsection{加载}
\subsection{真实世界的vmlinux}
为了给大家呈现一个简单的世界，作者自己实现了一个内核加载程序。
这个程序加载简化的内核足够用了，但对于真实的linux内核，他是绝对不够用的。
原因很简单，vmlinux如果包含调试信息的话有200M那么大，在实模式下是绝对无法加载的。
真实世界的linux通常是由GRUB加载，当然还有其他的加载器。
GRUB基本上已经是一个小型的操作系统了。
它支持与Linux相同的驱动程序框架，并且包含了很多驱动程序，比如ext2,msdos等文件系统的驱动。
当然为了统一支持他们，还需要VFS。
此外还携带一个shell。
当然它不支持操作系统的高级特性，比如复杂的内存管理，多进程的切换和调度，它都没有。
但它一定是运行在保护模式下的，在完成加载之后回到实模式，而后将控制权交给目标操作系统。
在我们的简化操作系统基本成型以后，我们会尝试使用GRUB2来加载我们的系统。
\section{调试技巧}
只要是程序就一定有bug。尤其是按照前面我们所鼓励的方式：将代码抄进文件编译运行。
如果有时间作者强烈推荐这种方式，它能够增强你对系统的理解。
但这种方式一定会让你的程序存在bug，之前已经介绍了qemu自身就带有gdbstub。让我们一起感受他的强大吧。
\subsection{gdb远程调试}
\subsection{symbol-file}
前面介绍过，启动程序必须控制在512字节，而且它必须是binary格式的。
所谓binary格式就是CPU可以从第一个字节开始运行的程序。
因此调试信息是不可能存在与binary之上的。
不过大家不用担心，因为我们还生成了一个ELF文件，这个文件包含完整的调试信息。
\begin{lstlisting}[language=bash]
 target remote :1234
 symbol-file out/arch/i386/boot/bootsect.gdb
 hbreak *0x7c00
\end{lstlisting}
\subsection{基本原理}
在调试之前，我必须首先给大家讲解一下gdb远程调试的基本原理。
实际上gdb远程调试从架构上将是一个胖客户端程序，服务器仅仅回传最基本的信息。
这些信息基本上就是各类寄存器的值。
而gdb客户端通过eip的内容及带有完整调试信息的elf文件，就可以分析出当前在那个函数运行。
设置断点的原理也是类似的，如果读者对它感兴趣可以参考附录当中的章节。
